<!DOCTYPE html>
<!-- saved from url=(0054)https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0 -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- <geektime>1591721827627</geektime> -->
    
    
        <title>NIO看破也说破（四）—— Java的NIO_Java_小眼睛聊技术_InfoQ写作社区</title>
        <meta name="description" content="Java的NIO有selector，系统内核也提供了多种非阻塞IO模型，Java社区也出现了像netty这种优秀的 NIO 框架。Java的NIO 与内核的阻塞模型到底什么关系，为什么Java有NIO" id="metadesc">
    
    <meta name="keywords" content="netty,NIO,Java,模型,框架,看破,说破,selector,提供,IO,社区,了像,优秀,内核,阻塞,关系,API,网上,reactor" id="metakeywords">
    <meta property="og:image" content="https://static001.infoq.cn/static/write/img/write-favicon.jpg">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
    
        <link rel="icon" type="image/png" sizes="32x32" href="https://static001.infoq.cn/static/write/img/write-favicon.jpg">
        <link rel="icon" type="image/png" sizes="16x16" href="https://static001.infoq.cn/static/write/img/write-favicon.jpg">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="format-detection" content="telephone=no">
    <meta name="applicable-device" content="pc,mobile">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <link rel="canonical" href="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0">
    <meta property="og:title" content="NIO看破也说破（四）—— Java的NIO_Java_小眼睛聊技术_InfoQ写作社区">
    <meta property="og:url" content="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0">
    <meta property="og:description" content="Java的NIO有selector，系统内核也提供了多种非阻塞IO模型，Java社区也出现了像netty这种优秀的 NIO 框架。Java的NIO 与内核的阻塞模型到底什么关系，为什么Java有NIO">

    <script type="text/javascript" async="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/js"></script><script type="text/javascript" async="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/js(1)"></script><script async="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/sensorsdata.min.js.下载" charset="UTF-8"></script><script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/hm.js.下载"></script><script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/hm(1).js.下载"></script><script async="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/analytics.js.下载"></script><script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/push.js.下载"></script><script type="text/javascript" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/jweixin-1.4.0.js.下载"></script>
    <script type="text/javascript" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/bufferLogger.1.0.8.min.js.下载"></script>
    <script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/dingtalk.open.js.下载"></script>
    
        <script type="text/javascript" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/aliplayer-min.js.下载"></script>
        <script type="text/javascript" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/hls.8361666e7e2690d988ff5fe775b62bbe.js.下载"></script>
    
        
    <script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/jquery-1.12.4.min(1).js.下载"></script>
    
    <script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/mantis.min.js.下载"></script>
    <script type="text/javascript">
        if (!('flex' in document.documentElement.style) && !navigator.userAgent.match(/spider|googlebot|bingbot|geekbang|yahoo! Slurp/i)) {
            window.location.href = '//static001.geekbang.org/static/common/browser_update/index.html'
        }
    </script>
    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https'){
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else{
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>
<link href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/app.f787f7a6.css" rel="stylesheet"><script src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/1725952827-mantis_probe.min.js.下载" type="text/javascript" defer=""></script><meta name="referrer" content="no-referrer-when-downgrade"><script charset="utf-8" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-codemirror.5b03effd.js.下载"></script><link rel="stylesheet" type="text/css" href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-katex.ce0dccd9.css"><script charset="utf-8" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-katex.7cd46588.js.下载"></script><link rel="stylesheet" type="text/css" href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-geek-editor.b88154cf.css"><script charset="utf-8" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-geek-editor.9ca1b5f5.js.下载"></script><link rel="stylesheet" type="text/css" href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-12b29e91.053549ea.css"><script charset="utf-8" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/chunk-12b29e91.1598f130.js.下载"></script><link rel="stylesheet" type="text/css" href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/PageArticle.76a73b25.css"><script charset="utf-8" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/PageArticle.9509fe6c.js.下载"></script></head>

<body>
<div data-v-b110a92e="" data-v-4a1c10f0="" id="layout" layout="" class="page-article-layout ready skin-base size-base" style="padding-bottom: 0px;"><div data-v-b110a92e="" layout-header-wrap="" class="layout-header-wrap"><div data-v-2c781bfe="" data-v-b110a92e="" class="common-header-pc"><div data-v-2c781bfe="" class="shim"></div><div data-v-2c781bfe="" class="header"><div data-v-2c781bfe="" class="wrap"><div data-v-2c781bfe="" class="layout-content header-content"><div data-v-2c781bfe="" class="logo"><a data-v-2c781bfe="" href="https://www.infoq.cn/" class="main-logo"><img data-v-2c781bfe="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/logo.b51e49df.png" alt="logo"></a><a data-v-2c781bfe="" href="https://xie.infoq.cn/" class="sub-logo"><img data-v-2c781bfe="" src="" alt="logo"></a></div><div data-v-2c781bfe="" class="header-middle"><div data-v-2c781bfe="" class="nav"><ul data-v-2c781bfe=""><li data-v-2c781bfe=""><a data-v-2c781bfe="" href="https://xie.infoq.cn/" class="">首页</a></li><li data-v-2c781bfe=""><!----><a data-v-2c781bfe="" href="https://xie.infoq.cn/square/13" target="_blank" rel="" class=""> 玩转社区 </a></li><li data-v-2c781bfe=""><!----><a data-v-2c781bfe="" href="https://xie.infoq.cn/tag/3" target="_blank" rel="" class=""> Java </a></li><li data-v-2c781bfe=""><!----><a data-v-2c781bfe="" href="https://xie.infoq.cn/tag/211" target="_blank" rel="" class=""> 开源 </a></li><li data-v-2c781bfe=""><!----><a data-v-2c781bfe="" href="https://xie.infoq.cn/tag/238" target="_blank" rel="" class=""> 架构 </a></li><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----></ul><div data-v-2c781bfe="" class="more"> 全部标签 <i data-v-2c781bfe="" class="iconfont"></i></div></div><div data-v-2c781bfe="" class="search-container"><div data-v-2c781bfe="" class="search-wrap"><input data-v-2c781bfe="" type="text" placeholder="搜索" class="search-input"><span data-v-2c781bfe="" class="search-btn iconfont"></span></div></div><!----><div data-v-2c781bfe="" class="write-btn-warp"><div data-v-2c781bfe="" gk-button="" gkbtn-color="green" gkbtn-size="small" class="write-btn Button_button_3onsJ"> 写点什么 </div><div data-v-2c781bfe="" class="scene" style="display: none;"><h4 data-v-2c781bfe=""><i data-v-2c781bfe="" class="iconfont"></i>创作场景</h4><div data-v-2c781bfe="" class="close"><i data-v-2c781bfe="" class="iconfont"></i></div><ul data-v-2c781bfe=""><li data-v-2c781bfe="">记录自己日常工作的实践、心得</li><li data-v-2c781bfe="">发表对生活和职场的感悟</li><li data-v-2c781bfe="">针对感兴趣的事件发表随笔或者杂谈</li><li data-v-2c781bfe="">从0到1详细介绍你掌握的一门语言、一个技术，或者一个兴趣、爱好</li><li data-v-2c781bfe="">或者，就直接把你的个人博客、公众号直接搬到这里</li></ul></div></div></div><div data-v-2c781bfe="" class="account"><span data-v-2c781bfe="" class="route">登录</span><span data-v-2c781bfe="" class="line"></span><span data-v-2c781bfe="" class="route">注册</span></div></div></div><div data-v-2c781bfe="" class="more-tags"><div data-v-2c781bfe="" class="inner-content"><div data-v-4300d745="" data-v-2c781bfe="" class="com-label-list"><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/9434" com-label-title="" class="com-label-title">OpenHarmony</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/303" com-label-title="" class="com-label-title">算法</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/14135" com-label-title="" class="com-label-title">元宇宙</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/84" com-label-title="" class="com-label-title">MySQL</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/5237" com-label-title="" class="com-label-title">移动开发</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/221" com-label-title="" class="com-label-title">学习方法</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/221" com-label-title="" class="com-label-title">学习方法</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/17178" com-label-title="" class="com-label-title">Web3.0</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/203" com-label-title="" class="com-label-title">高效工作</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/92" com-label-title="" class="com-label-title">数据库</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/8" com-label-title="" class="com-label-title">Python</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/661" com-label-title="" class="com-label-title">音视频</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/371" com-label-title="" class="com-label-title">前端</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/242" com-label-title="" class="com-label-title">AI</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/143" com-label-title="" class="com-label-title">大数据</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/204" com-label-title="" class="com-label-title">团队管理</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/232" com-label-title="" class="com-label-title">程序员</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/317" com-label-title="" class="com-label-title">运维</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/200" com-label-title="" class="com-label-title">深度思考</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/1081" com-label-title="" class="com-label-title">低代码</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/97" com-label-title="" class="com-label-title">redis</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/19" com-label-title="" class="com-label-title">golang</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/294" com-label-title="" class="com-label-title">微服务架构</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/21" com-label-title="" class="com-label-title">flutter</a><a data-v-2c781bfe="" data-v-4300d745="" href="https://xie.infoq.cn/tag" target="_blank" class="more-tag com-label-title"> 查看更多 <i data-v-2c781bfe="" data-v-4300d745="" class="iconfont"></i></a></div></div></div></div></div></div><div data-v-b110a92e="" inner-content="" class="inner-content"><!----><!----><!----><div data-v-4a1c10f0="" data-v-b110a92e="" class="layout-content article-content"><div data-v-4a1c10f0="" class="article-fixed-wrap" data-v-b110a92e="" style="width: 150px;"><div data-v-4a1c10f0="" class="side-operation" style="top: 91px;"><div data-v-4a51d84b="" data-v-4a1c10f0="" class="widget-operation side" wxshareurl="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0" authorinfo="[object Object]"><div data-v-4a51d84b="" class="toggle-menu btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" class="like btn"><i data-v-4a51d84b="" class="iconfont"></i><i data-v-4a51d84b="" class="like-num">14</i></div><div data-v-4a51d84b="" class="favorite btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" class="comment btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" class="weixin btn"><span data-v-4a51d84b="" class="gkui-popup share-popup-left share-popup"><i data-v-4a51d84b="" class="iconfont"></i><!----></span></div><div data-v-4a51d84b="" class="weibo btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" class="horde btn"><i data-v-4a51d84b="" class="iconfont"></i></div><!----><!----></div></div></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="main"><!----><!----><h1 data-v-4a1c10f0="" data-v-b110a92e="" class="article-title">NIO 看破也说破（四）—— Java 的 NIO</h1><div data-v-4a1c10f0="" data-v-b110a92e="" class="author-information"><div data-v-4a1c10f0="" data-v-b110a92e="" class="row"><div data-v-4a1c10f0="" data-v-b110a92e="" class="author-name"> 作者：<span data-v-3bc80670="" data-v-6281d13f="" data-v-4a1c10f0="" class="ctn" data-v-b110a92e=""><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span></div></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="row article-head"><ul data-v-4a1c10f0="" data-v-b110a92e="" class="read-time"><li data-v-4a1c10f0="" data-v-b110a92e="">2020-05-19  <!----></li><li data-v-4a1c10f0="" data-v-b110a92e=""><p data-v-4a1c10f0="" data-v-b110a92e="" class="time-style">本文字数：10536 字 </p><!----><p data-v-4a1c10f0="" data-v-b110a92e="" class="time-style">阅读完需：约 35 分钟</p></li></ul></div></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="article-cover"><img data-v-4a1c10f0="" data-v-b110a92e="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/be7d174cee110613cb35f915b8790c46.jpeg" alt="NIO看破也说破（四）—— Java的NIO"></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="article-detaile"><div data-v-fbbc0218="" data-v-4a1c10f0="" class="article-preview-content size-base" data-v-b110a92e=""><div data-v-fbbc0218="" data-type="doc" class="article-preview"><div contenteditable="false" translate="no" class="ProseMirror"><p data-type="paragraph">Java 的 NIO 有 selector，系统内核也提供了多种非阻塞 IO 模型，Java 社区也出现了像 netty 这种优秀的 NIO 框架。Java 的 NIO 与内核的阻塞模型到底什么关系，为什么 Java 有 NIO 的 API 还出现了 netty 这种框架，网上说的 reactor 到底是什么？本文通过分析代码，带你一步步搞清楚 Java 的 NIO 和系统函数之间的关系，以及 Java NIO 是如何一步步衍生出来 netty 框架。</p><h2 data-id="heading0" data-type="heading" top="331">NIO 概念</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">前几节我们提到了 <i data-type="italic">Nonblocking IO</i> 的概念，在 Java 中有 Java NIO 的系列包，网上的大多数资料把 Java 的 NIO 等同于 <i data-type="italic">Nonblocking IO</i> ，这是错误的。Java 中的 NIO 指的是从 1.4 版本后，提供的一套可以替代标准的 Java IO 的 new API。有三部分组成：</p><ul data-type="bulletedlist"><li><p data-type="paragraph">Buffer 缓冲区</p></li><li><p data-type="paragraph">Channel 通道</p></li><li><p data-type="paragraph">Selector 选择器</p></li></ul><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">API 的具体使用不在本文赘述。</p><h2 data-id="heading1" data-type="heading" top="675">代码模板</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">NIO 大致分为这几步骤：</p><ol data-type="numberedlist"><li><p data-number="1" data-type="paragraph">获取 channel</p></li><li><p data-number="2" data-type="paragraph">设置非阻塞</p></li><li><p data-number="3" data-type="paragraph">创建多路复用器 selector</p></li><li><p data-number="4" data-type="paragraph">channel 和 selector 做关联</p></li><li><p data-number="5" data-type="paragraph">根据 selector 返回的 channel 状态处理逻辑</p></li></ol><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 开启一个channel</span></code><code data-type="codeline"><span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> ServerSocketChannel.open();</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 设置为非阻塞</span></code><code data-type="codeline">serverSocketChannel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 绑定端口</span></code><code data-type="codeline">serverSocketChannel.bind(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InetSocketAddress</span>(PORT));</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 打开一个多路复用器</span></code><code data-type="codeline"><span data-type="markclass" class="hljs-type">Selector</span> <span data-type="markclass" class="hljs-variable">selector</span> <span data-type="markclass" class="hljs-operator">=</span> Selector.open();</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 绑定多路复用器和channel</span></code><code data-type="codeline">serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 获取到达的事件</span></code><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">while</span> (selector.select() &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">    Set&lt;SelectionKey&gt; keys = selector.keys();</code><code data-type="codeline">    Iterator&lt;SelectionKey&gt; iterator = keys.iterator();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-comment">// 处理逻辑</span></code><code data-type="codeline">        }</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isReadable()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-comment">// 处理逻辑</span></code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 490px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div><div data-codeblock-number="23"></div><div data-codeblock-number="24"></div></div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading2" data-type="heading" top="1540.5999755859375">单线程示例</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">参考代码模板，我们用 NIO 实现一个 Echo Server。server 代码如下：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">main</span><span data-type="markclass" class="hljs-params">(String[] args)</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">Selector</span> <span data-type="markclass" class="hljs-variable">selector</span> <span data-type="markclass" class="hljs-operator">=</span> initServer();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">while</span> (selector.select() &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">        Set&lt;SelectionKey&gt; set = selector.selectedKeys();</code><code data-type="codeline">        Iterator&lt;SelectionKey&gt; iterator = set.iterator();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> </code><code data-type="codeline">                        (ServerSocketChannel) selectionKey.channel();</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> serverSocketChannel.accept();</code><code data-type="codeline">                    System.out.println(<span data-type="markclass" class="hljs-string">"建立链接："</span> + channel.getRemoteAddress());</code><code data-type="codeline">                    channel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> ByteBuffer.allocate(<span data-type="markclass" class="hljs-number">1024</span>);</code><code data-type="codeline">                    channel.register(selector, SelectionKey.OP_READ, buffer);</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">else</span> <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isReadable()) {</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> (SocketChannel) selectionKey.channel();</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> (ByteBuffer) selectionKey.attachment();</code><code data-type="codeline">                    buffer.clear();</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">StringBuilder</span> <span data-type="markclass" class="hljs-variable">sb</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">StringBuilder</span>();</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-type">int</span> <span data-type="markclass" class="hljs-variable">read</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-number">0</span>;</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-keyword">while</span> ((read = channel.read(buffer)) &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">                        buffer.flip();</code><code data-type="codeline">                        sb.append(Charset.forName(<span data-type="markclass" class="hljs-string">"UTF-8"</span>).</code><code data-type="codeline">                                  newDecoder().decode(buffer));</code><code data-type="codeline">                        buffer.clear();</code><code data-type="codeline">                    }</code><code data-type="codeline">                    System.out.printf(<span data-type="markclass" class="hljs-string">"收到 %s 发来的：%s\n"</span>, </code><code data-type="codeline">                                      channel.getRemoteAddress(), sb);</code><code data-type="codeline">                    buffer.clear();</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-comment">// 模拟server端处理耗时</span></code><code data-type="codeline">                    Thread.sleep((<span data-type="markclass" class="hljs-type">int</span>) (Math.random() * <span data-type="markclass" class="hljs-number">1000</span>));</code><code data-type="codeline">                    buffer.put((<span data-type="markclass" class="hljs-string">"收到，你发来的是："</span> + sb + <span data-type="markclass" class="hljs-string">"\r\n"</span>).getBytes(<span data-type="markclass" class="hljs-string">"utf-8"</span>));</code><code data-type="codeline">                    buffer.flip();</code><code data-type="codeline">                    channel.write(buffer);</code><code data-type="codeline">                    System.out.printf(<span data-type="markclass" class="hljs-string">"回复 %s 内容是： %s\n"</span>, </code><code data-type="codeline">                                      channel.getRemoteAddress(), sb);</code><code data-type="codeline">                    channel.register(selector, SelectionKey.OP_READ, buffer.clear());</code><code data-type="codeline">                }</code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException | InterruptedException e) {</code><code data-type="codeline">                selectionKey.cancel();</code><code data-type="codeline">                selectionKey.channel().close();</code><code data-type="codeline">                System.err.println(e.getMessage());</code><code data-type="codeline">            }</code><code data-type="codeline">            iterator.remove();</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 990px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div><div data-codeblock-number="23"></div><div data-codeblock-number="24"></div><div data-codeblock-number="25"></div><div data-codeblock-number="26"></div><div data-codeblock-number="27"></div><div data-codeblock-number="28"></div><div data-codeblock-number="29"></div><div data-codeblock-number="30"></div><div data-codeblock-number="31"></div><div data-codeblock-number="32"></div><div data-codeblock-number="33"></div><div data-codeblock-number="34"></div><div data-codeblock-number="35"></div><div data-codeblock-number="36"></div><div data-codeblock-number="37"></div><div data-codeblock-number="38"></div><div data-codeblock-number="39"></div><div data-codeblock-number="40"></div><div data-codeblock-number="41"></div><div data-codeblock-number="42"></div><div data-codeblock-number="43"></div><div data-codeblock-number="44"></div><div data-codeblock-number="45"></div><div data-codeblock-number="46"></div><div data-codeblock-number="47"></div><div data-codeblock-number="48"></div><div data-codeblock-number="49"></div></div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">写一个 client ，模拟 50 个线程同时请求 server 端，在 readHandler 中模拟了随机 sleep。client 代码：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">main</span><span data-type="markclass" class="hljs-params">(String[] args)</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">for</span> (<span data-type="markclass" class="hljs-type">int</span> <span data-type="markclass" class="hljs-variable">i</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-number">0</span>; i &lt; <span data-type="markclass" class="hljs-number">50</span>; i++) {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Thread</span>(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Runnable</span>() {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">run</span><span data-type="markclass" class="hljs-params">()</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                    clientHandler();</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                    e.printStackTrace();</code><code data-type="codeline">                }</code><code data-type="codeline">            }</code><code data-type="codeline">        }).start();</code><code data-type="codeline">    }</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">return</span>;</code><code data-type="codeline">}</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">clientHandler</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">long</span> <span data-type="markclass" class="hljs-variable">start</span> <span data-type="markclass" class="hljs-operator">=</span> System.currentTimeMillis();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">Socket</span> <span data-type="markclass" class="hljs-variable">socket</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Socket</span>();</code><code data-type="codeline">    socket.setSoTimeout(<span data-type="markclass" class="hljs-number">10000</span>);</code><code data-type="codeline">    socket.connect(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InetSocketAddress</span>(<span data-type="markclass" class="hljs-number">9999</span>));</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">OutputStream</span> <span data-type="markclass" class="hljs-variable">outputStream</span> <span data-type="markclass" class="hljs-operator">=</span> socket.getOutputStream();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">BufferedWriter</span> <span data-type="markclass" class="hljs-variable">bw</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">BufferedWriter</span>(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">OutputStreamWriter</span>(outputStream));</code><code data-type="codeline">    bw.write(<span data-type="markclass" class="hljs-string">"你好，我是client "</span> + socket.getLocalSocketAddress() + <span data-type="markclass" class="hljs-string">"\r\n"</span>);</code><code data-type="codeline">    bw.flush();</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">InputStream</span> <span data-type="markclass" class="hljs-variable">inputStream</span> <span data-type="markclass" class="hljs-operator">=</span> socket.getInputStream();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">BufferedReader</span> <span data-type="markclass" class="hljs-variable">br</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">BufferedReader</span>(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InputStreamReader</span>(inputStream));</code><code data-type="codeline">    System.out.printf(<span data-type="markclass" class="hljs-string">"接到服务端响应：%s，处理了%d\r\n"</span>, br.readLine(), (System.currentTimeMillis() - start));</code><code data-type="codeline">    br.close();</code><code data-type="codeline">    inputStream.close();</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    bw.close();</code><code data-type="codeline">    outputStream.close();</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 710px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="width: 712px; display: block; transform: translate3d(0px, 0px, 0px);"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div><div data-codeblock-number="23"></div><div data-codeblock-number="24"></div><div data-codeblock-number="25"></div><div data-codeblock-number="26"></div><div data-codeblock-number="27"></div><div data-codeblock-number="28"></div><div data-codeblock-number="29"></div><div data-codeblock-number="30"></div><div data-codeblock-number="31"></div><div data-codeblock-number="32"></div><div data-codeblock-number="33"></div><div data-codeblock-number="34"></div><div data-codeblock-number="35"></div></div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/75660df7f026b638b7aa9c78098fc069.png"> <div data-title="">Java NIO 单线程</div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading3" data-type="heading" top="3704.800048828125">Selector 实现原理</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">用 strace 启动，<code data-type="codeinline">[root@f00e68119764 tmp]# strace -ff -o out /usr/lib/jvm/java-1.8.0/bin/java NIOServerSingle</code> 分析执行的过程，在日志中可以看到如下片段：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="shell"><code data-type="codeline">20083 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 4</code><code data-type="codeline">20084 setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0</code><code data-type="codeline">20085 clock_gettime(CLOCK_MONOTONIC, {tv_sec=242065, tv_nsec=887240727}) = 0</code><code data-type="codeline">20086 fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)</code><code data-type="codeline">20087 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK)    = 0</code><code data-type="codeline">20088 <span data-type="markclass" class="hljs-built_in">bind</span>(4, {sa_family=AF_INET, sin_port=htons(9999), sin_addr=inet_addr(<span data-type="markclass" class="hljs-string">"0.0.0.0"</span>)}, 16) = 0</code><code data-type="codeline">20089 listen(4, 50)                           = 0</code><code data-type="codeline">20090 getsockname(4, {sa_family=AF_INET, sin_port=htons(9999), sin_addr=inet_addr(<span data-type="markclass" class="hljs-string">"0.0.0.0"</span>)}, [16]) = 0</code><code data-type="codeline">20091 getsockname(4, {sa_family=AF_INET, sin_port=htons(9999), sin_addr=inet_addr(<span data-type="markclass" class="hljs-string">"0.0.0.0"</span>)}, [16]) = 0</code><code data-type="codeline">20092 epoll_create(256)                       = 7</code><code data-type="codeline">21100 epoll_ctl(7, EPOLL_CTL_ADD, 4, {EPOLLIN, {u32=4, u64=158913789956}}) = 0</code><code data-type="codeline">21101 epoll_wait(7, [{EPOLLIN, {u32=4, u64=158913789956}}], 8192, -1) = 1</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 250px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="width: 731px; display: block; transform: translate3d(0px, 0px, 0px);"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div></div></div></div><p data-type="paragraph">可以看出，在 Java 的 NIO 中（java1.8）底层是调用的系统 epoll ，关于 epoll 请<a href="https://xie.infoq.cn/article/628ae27da9ccb37d2900e8ef4" title="" data-type="link">出门右转</a> 这里不再啰嗦。</p><p data-type="paragraph">从源码中也可以看出：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> Selector <span data-type="markclass" class="hljs-title function_">open</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">return</span> SelectorProvider.provider().openSelector();</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 70px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div></div></div></div><p data-type="paragraph">openSelector 是抽象方法具体实现类，在 Linux 上代码如下：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">EPollSelectorProvider</span></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">extends</span> <span data-type="markclass" class="hljs-title class_">SelectorProviderImpl</span></code><code data-type="codeline">{</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> AbstractSelector <span data-type="markclass" class="hljs-title function_">openSelector</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">return</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">EPollSelectorImpl</span>(<span data-type="markclass" class="hljs-built_in">this</span>);</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> Channel <span data-type="markclass" class="hljs-title function_">inheritedChannel</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">return</span> InheritedChannel.getChannel();</code><code data-type="codeline">    }</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 230px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div></div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">跟踪代码可以看到最后调用 native 方法，说明：<strong data-type="strong">Java NIO 是利用系统内核提供的能力。</strong></p><h2 data-id="heading4" data-type="heading" top="4756.80029296875">多线程处理</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">我们把单线程示例中，readHandler 随机 sleep，稍稍做些修改。模拟 server 端执行某一次请求时，处理过慢，如图示：</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">第十五个请求过来时，随机 sleep：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 模拟server端处理耗时</span></code><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">if</span> (t.get() == <span data-type="markclass" class="hljs-number">15</span>) {</code><code data-type="codeline">    Thread.sleep((<span data-type="markclass" class="hljs-type">int</span>) (Math.random() * <span data-type="markclass" class="hljs-number">10000</span>));</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 90px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div></div></div></div><p data-type="paragraph">结果第十五个线程之后，所有 client 的执行都有一个短暂的等待</p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/0c72de43003a224ef3dfd057938ae7c9.png"> <!----></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">很容易解释，因为在单线程处理中，channel 创建、IO 读写均为一个 Thread ，面对 50 个 client，IO 时间需要排队处理。因此我们 Redis 系列中也提到了在 Redis 中，尽量避免某一个 key 的操作会很耗时的情况。可以参考 <a href="https://xie.infoq.cn/article/91ab6a27e9bca957cab2d1819" title="" data-type="link">出门右转</a> </p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">我们对代码做一些改造，client 端代码不动，server 端代码稍作调整。增加一个线程来处理读写时间，代码片段如下：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> </code><code data-type="codeline">        (ServerSocketChannel) selectionKey.channel();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> serverSocketChannel.accept();</code><code data-type="codeline">    System.out.println(<span data-type="markclass" class="hljs-string">"建立链接："</span> + channel.getRemoteAddress());</code><code data-type="codeline">    channel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> ByteBuffer.allocate(<span data-type="markclass" class="hljs-number">1024</span>);</code><code data-type="codeline">    channel.register(selector, SelectionKey.OP_READ, buffer);</code><code data-type="codeline">} <span data-type="markclass" class="hljs-keyword">else</span> <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isReadable()) {</code><code data-type="codeline">    service.execute(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Runnable</span>() {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">run</span><span data-type="markclass" class="hljs-params">()</span> {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-comment">// 处理逻辑……………………</span></code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                e.printStackTrace();</code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (InterruptedException e) {</code><code data-type="codeline">                e.printStackTrace();</code><code data-type="codeline">            }</code><code data-type="codeline">        }</code><code data-type="codeline">    });</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 450px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div></div></div></div><p data-type="paragraph">这样相当于 server 端有两个线程，一个是主线程启动的 selector 来监听 channel 的 OP_ACCEPT 状态，另一个线程是处理 channel 的读写。程序也可以继续执行，稍稍快了一些。</p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/12ab13dd8c9ce27e4e84fa44d8c1e884.png"> <div data-title="">Java NIO 多线程</div></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading5" data-type="heading" top="6110">reactor 模式</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">接触 NIO 就一定听过 reactor 这个名词，reactor 经常被混入 NIO 中，让很多人混淆概念。Reactor 到底是什么，<a href="https://xie.infoq.cn/link?target=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FReactor_pattern%23cite_note-1" title="" data-type="link">维基百科</a>的解释：</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><blockquote data-type="blockquote"><p data-type="paragraph">The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.</p></blockquote><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">核心点四个：</p><ol data-type="numberedlist"><li><p data-number="1" data-type="paragraph">reactor design pattern （reactor 是一种设计模式，不是专属于某个语言或框架的）</p></li><li><p data-number="2" data-type="paragraph">event handling pattern （事件处理模式）</p></li><li><p data-number="3" data-type="paragraph">delivered concurrently to a service handler by one or more inputs（一次处理一个或多个输入）</p></li><li><p data-number="4" data-type="paragraph">demultiplexes the incoming requests and dispatches them（多路分解，分发）</p></li></ol><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">我们对单线程示例做些修改：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">main</span><span data-type="markclass" class="hljs-params">(String[] args)</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">Selector</span> <span data-type="markclass" class="hljs-variable">selector</span> <span data-type="markclass" class="hljs-operator">=</span> initServer();</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">while</span> (selector.select() &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">        Set&lt;SelectionKey&gt; set = selector.selectedKeys();</code><code data-type="codeline">        Iterator&lt;SelectionKey&gt; iterator = set.iterator();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">            dispatcher(selectionKey);</code><code data-type="codeline">            iterator.remove();</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 250px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div></div></div></div><p data-type="paragraph">initServer 的实现：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">static</span> Selector <span data-type="markclass" class="hljs-title function_">initServer</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverChannel</span> <span data-type="markclass" class="hljs-operator">=</span> ServerSocketChannel.open();</code><code data-type="codeline">    serverChannel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">    serverChannel.bind(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InetSocketAddress</span>(<span data-type="markclass" class="hljs-number">9999</span>));</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-type">Selector</span> <span data-type="markclass" class="hljs-variable">selector</span> <span data-type="markclass" class="hljs-operator">=</span> Selector.open();</code><code data-type="codeline">    serverChannel.register(selector, SelectionKey.OP_ACCEPT);</code><code data-type="codeline">    System.out.println(<span data-type="markclass" class="hljs-string">"server 启动"</span>);</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">return</span> selector;</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 210px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div></div></div></div><p data-type="paragraph">dispatcher 的实现：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">dispatcher</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-comment">// 我只负责处理链接</span></code><code data-type="codeline">            acceptHandler(selector, selectionKey);</code><code data-type="codeline">        } <span data-type="markclass" class="hljs-keyword">else</span> <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isReadable()) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-comment">// 我只处理读写数据</span></code><code data-type="codeline">            readHandler(selector, selectionKey);</code><code data-type="codeline">        }</code><code data-type="codeline">    } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException | InterruptedException e) {</code><code data-type="codeline">        selectionKey.cancel();</code><code data-type="codeline">        selectionKey.channel().close();</code><code data-type="codeline">        System.err.println(e.getMessage());</code><code data-type="codeline">    }</code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 310px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div></div></div></div><p data-type="paragraph">acceptHandler 的实现：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> (ServerSocketChannel) selectionKey.channel();</code><code data-type="codeline"><span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> serverSocketChannel.accept();</code><code data-type="codeline">System.out.println(<span data-type="markclass" class="hljs-string">"建立链接："</span> + channel.getRemoteAddress());</code><code data-type="codeline">channel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline"><span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> ByteBuffer.allocate(<span data-type="markclass" class="hljs-number">1024</span>);</code><code data-type="codeline">channel.register(selector, SelectionKey.OP_READ, buffer);</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 130px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div></div></div></div><p data-type="paragraph">readHandler 的实现：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> (SocketChannel) selectionKey.channel();</code><code data-type="codeline"><span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> (ByteBuffer) selectionKey.attachment();</code><code data-type="codeline">buffer.clear();</code><code data-type="codeline"><span data-type="markclass" class="hljs-type">StringBuilder</span> <span data-type="markclass" class="hljs-variable">sb</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">StringBuilder</span>();</code><code data-type="codeline"><span data-type="markclass" class="hljs-type">int</span> <span data-type="markclass" class="hljs-variable">read</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-number">0</span>;</code><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">while</span> ((read = channel.read(buffer)) &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">    buffer.flip();</code><code data-type="codeline">    sb.append(Charset.forName(<span data-type="markclass" class="hljs-string">"UTF-8"</span>).newDecoder().decode(buffer));</code><code data-type="codeline">    buffer.clear();</code><code data-type="codeline">}</code><code data-type="codeline">System.out.printf(<span data-type="markclass" class="hljs-string">"收到 %s 发来的：%s\n"</span>, channel.getRemoteAddress(), sb);</code><code data-type="codeline">buffer.clear();</code><code data-type="codeline"><span data-type="markclass" class="hljs-comment">// 模拟server端处理耗时</span></code><code data-type="codeline">Thread.sleep((<span data-type="markclass" class="hljs-type">int</span>) (Math.random() * <span data-type="markclass" class="hljs-number">1000</span>));</code><code data-type="codeline">buffer.put((<span data-type="markclass" class="hljs-string">"收到，你发来的是："</span> + sb + <span data-type="markclass" class="hljs-string">"\r\n"</span>).getBytes(<span data-type="markclass" class="hljs-string">"utf-8"</span>));</code><code data-type="codeline">buffer.flip();</code><code data-type="codeline">channel.write(buffer);</code><code data-type="codeline">System.out.printf(<span data-type="markclass" class="hljs-string">"回复 %s 内容是： %s\n"</span>, channel.getRemoteAddress(), sb);</code><code data-type="codeline">channel.register(selector, SelectionKey.OP_READ, buffer.clear());</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 390px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div></div></div></div><h3 data-id="heading6" data-type="heading" top="8352">单线程 Reactor</h3><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">改造后的代码，具备了以下特点：</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><ol data-type="numberedlist"><li><p data-number="1" data-type="paragraph">基于事件驱动（ NIO 的 selector，底层对事件驱动的 epoll 实现 jdk1.8）</p></li><li><p data-number="2" data-type="paragraph">统一分派中心（dispatcher 方法）</p></li><li><p data-number="3" data-type="paragraph">不同的事件处理（accept 和 read write 拆分）</p></li></ol><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">已经基本上实现了 Reactor 的单线程模式，我们把示例代码再做一些改造：</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">ReactorDemo</span> {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> Selector selector;</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-title function_">ReactorDemo</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        initServer();</code><code data-type="codeline">    }</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">initServer</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverChannel</span> <span data-type="markclass" class="hljs-operator">=</span> ServerSocketChannel.open();</code><code data-type="codeline">        serverChannel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">        serverChannel.bind(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InetSocketAddress</span>(<span data-type="markclass" class="hljs-number">9999</span>));</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        selector = Selector.open();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> serverChannel.</code><code data-type="codeline">            register(selector, SelectionKey.OP_ACCEPT);</code><code data-type="codeline">        selectionKey.attach(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Acceptor</span>());</code><code data-type="codeline">        System.out.println(<span data-type="markclass" class="hljs-string">"server 启动"</span>);</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">start</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">while</span> (selector.select() &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">            Set&lt;SelectionKey&gt; set = selector.selectedKeys();</code><code data-type="codeline">            Iterator&lt;SelectionKey&gt; iterator = set.iterator();</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">                dispater(selectionKey);</code><code data-type="codeline">                iterator.remove();</code><code data-type="codeline">            }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">dispater</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">Hander</span> <span data-type="markclass" class="hljs-variable">hander</span> <span data-type="markclass" class="hljs-operator">=</span> (Hander) selectionKey.attachment();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">if</span> (hander != <span data-type="markclass" class="hljs-literal">null</span>) {</code><code data-type="codeline">            hander.process(selectionKey);</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">interface</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span>;</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">Acceptor</span> <span data-type="markclass" class="hljs-keyword">implements</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> </code><code data-type="codeline">                    (ServerSocketChannel) selectionKey.channel();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> serverSocketChannel.accept();</code><code data-type="codeline">                System.out.println(<span data-type="markclass" class="hljs-string">"建立链接："</span> + channel.getRemoteAddress());</code><code data-type="codeline">                channel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">                selectionKey.attach(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">ProcessHander</span>());</code><code data-type="codeline">                channel.register(selector, SelectionKey.OP_READ);</code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                e.printStackTrace();</code><code data-type="codeline">            }</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">ProcessHandler</span> <span data-type="markclass" class="hljs-keyword">implements</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline">    </code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">main</span><span data-type="markclass" class="hljs-params">(String[] args)</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">ReactorDemo</span>().start();</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 1450px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div><div data-codeblock-number="23"></div><div data-codeblock-number="24"></div><div data-codeblock-number="25"></div><div data-codeblock-number="26"></div><div data-codeblock-number="27"></div><div data-codeblock-number="28"></div><div data-codeblock-number="29"></div><div data-codeblock-number="30"></div><div data-codeblock-number="31"></div><div data-codeblock-number="32"></div><div data-codeblock-number="33"></div><div data-codeblock-number="34"></div><div data-codeblock-number="35"></div><div data-codeblock-number="36"></div><div data-codeblock-number="37"></div><div data-codeblock-number="38"></div><div data-codeblock-number="39"></div><div data-codeblock-number="40"></div><div data-codeblock-number="41"></div><div data-codeblock-number="42"></div><div data-codeblock-number="43"></div><div data-codeblock-number="44"></div><div data-codeblock-number="45"></div><div data-codeblock-number="46"></div><div data-codeblock-number="47"></div><div data-codeblock-number="48"></div><div data-codeblock-number="49"></div><div data-codeblock-number="50"></div><div data-codeblock-number="51"></div><div data-codeblock-number="52"></div><div data-codeblock-number="53"></div><div data-codeblock-number="54"></div><div data-codeblock-number="55"></div><div data-codeblock-number="56"></div><div data-codeblock-number="57"></div><div data-codeblock-number="58"></div><div data-codeblock-number="59"></div><div data-codeblock-number="60"></div><div data-codeblock-number="61"></div><div data-codeblock-number="62"></div><div data-codeblock-number="63"></div><div data-codeblock-number="64"></div><div data-codeblock-number="65"></div><div data-codeblock-number="66"></div><div data-codeblock-number="67"></div><div data-codeblock-number="68"></div><div data-codeblock-number="69"></div><div data-codeblock-number="70"></div><div data-codeblock-number="71"></div><div data-codeblock-number="72"></div></div></div></div><p data-type="paragraph">我们实现了最基本的单 Reactor 的单线程模型，程序启动后 selector 负责获取、分离可用的 socket 交给 dispatcher 处理，dispatcher 交给不同的 handler 处理。其中 Acceptor 只负责 socket 链接，IO 的处理交给 ProcessHandler。</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">我把网上流传的 Reactor 的图，按自己的理解重新画了一份</p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/82b5c7e3ec790f57cbca6afed261b8ca.png"> <!----></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h3 data-id="heading7" data-type="heading" top="10402.6005859375">多线程 Reactor</h3><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">上面的示例代码中，从 socket 建立到 IO 完成，只有一个线程在处理。NIO 单线程示例中我们尝试加入线程池来加速 IO 任务的处理，reactor 模式中该如何实现呢？</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">简单理解，参考 NIO 多线程加入线程池处理所有的 processHandler ，可以利用 CPU 多核心加快业务处理，代码不再赘述。</p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/eb3d02db85408fc538a033138e5f98e5.png"> <!----></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h3 data-id="heading8" data-type="heading" top="10731.6005859375">多 Reactor 模式</h3><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">参考 ReactorDemo ，我们的 acceptor 处理 socket 链接时和 handler 处理 IO 都是用的同一个 selector 。如果我们在多线程基础上有两个 selector ，一个只负责处理 socket 链接一个处理网路 IO 各司其职将会更高大的提升系统吞吐量，该怎么实现呢？</p><div data-type="codeblock"><div data-codeblock-wrap=""><div data-codeblock-codes=""><div class="simplebar" data-simplebar="init"><div class="simplebar-wrapper" style="margin: 0px;"><div class="simplebar-height-auto-observer-wrapper"><div class="simplebar-height-auto-observer"></div></div> <div class="simplebar-mask"><div class="simplebar-offset" style="right: 0px; bottom: 0px;"><div class="simplebar-content-wrapper" style="height: auto; overflow: hidden scroll;"><div class="simplebar-content" style="padding: 0px;"><div><pre data-origin="pm_code_preview" lang="java"><code data-type="codeline"><span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">ReactorDemo</span> {</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> Selector selector;</code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> Selector ioSelector;</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-title function_">ReactorDemo</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        initServer();</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">initServer</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverChannel</span> <span data-type="markclass" class="hljs-operator">=</span> ServerSocketChannel.open();</code><code data-type="codeline">        serverChannel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">        serverChannel.bind(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">InetSocketAddress</span>(<span data-type="markclass" class="hljs-number">9999</span>));</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        selector = Selector.open();</code><code data-type="codeline">        ioSelector = Selector.open();</code><code data-type="codeline">        serverChannel.register(selector, SelectionKey.OP_ACCEPT);</code><code data-type="codeline">        System.out.println(<span data-type="markclass" class="hljs-string">"server 启动"</span>);</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">startServer</span><span data-type="markclass" class="hljs-params">()</span> {</code><code data-type="codeline">        Executors.newFixedThreadPool(<span data-type="markclass" class="hljs-number">1</span>).execute(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Runnable</span>() {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">run</span><span data-type="markclass" class="hljs-params">()</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                    majorListen();</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                    e.printStackTrace();</code><code data-type="codeline">                }</code><code data-type="codeline">            }</code><code data-type="codeline">        });</code><code data-type="codeline">        Executors.newFixedThreadPool(<span data-type="markclass" class="hljs-number">1</span>).execute(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Runnable</span>() {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">run</span><span data-type="markclass" class="hljs-params">()</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                    subListen();</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                    e.printStackTrace();</code><code data-type="codeline">                }</code><code data-type="codeline">            }</code><code data-type="codeline">        });</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">majorListen</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        System.out.println(<span data-type="markclass" class="hljs-string">"主selector启动"</span>);</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">while</span> (selector.select() &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">            System.out.println(<span data-type="markclass" class="hljs-string">"主selector有事件"</span>);</code><code data-type="codeline">            Set&lt;SelectionKey&gt; set = selector.selectedKeys();</code><code data-type="codeline">            Iterator&lt;SelectionKey&gt; iterator = set.iterator();</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">Acceptor</span>().process(selectionKey);</code><code data-type="codeline">                }</code><code data-type="codeline">                iterator.remove();</code><code data-type="codeline">            }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">subListen</span><span data-type="markclass" class="hljs-params">()</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        System.out.println(<span data-type="markclass" class="hljs-string">"子selector启动"</span>);</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">while</span> (<span data-type="markclass" class="hljs-literal">true</span>) {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">if</span> (ioSelector.select(<span data-type="markclass" class="hljs-number">100</span>) &lt;= <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">continue</span>;</code><code data-type="codeline">            }</code><code data-type="codeline">            System.out.println(<span data-type="markclass" class="hljs-string">"子selector有事件"</span>);</code><code data-type="codeline">            Set&lt;SelectionKey&gt; set = ioSelector.selectedKeys();</code><code data-type="codeline">            Iterator&lt;SelectionKey&gt; iterator = set.iterator();</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">while</span> (iterator.hasNext()) {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SelectionKey</span> <span data-type="markclass" class="hljs-variable">selectionKey</span> <span data-type="markclass" class="hljs-operator">=</span> iterator.next();</code><code data-type="codeline">                selectionKey.attach(<span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">ProcessHander</span>());</code><code data-type="codeline">                dispater(selectionKey, <span data-type="markclass" class="hljs-literal">true</span>);</code><code data-type="codeline">                iterator.remove();</code><code data-type="codeline">            }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">dispater</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey, <span data-type="markclass" class="hljs-type">boolean</span> isSub)</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-type">Hander</span> <span data-type="markclass" class="hljs-variable">hander</span> <span data-type="markclass" class="hljs-operator">=</span> (Hander) selectionKey.attachment();</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">if</span> (hander != <span data-type="markclass" class="hljs-literal">null</span>) {</code><code data-type="codeline">            hander.process(selectionKey);</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">interface</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span>;</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">Acceptor</span> <span data-type="markclass" class="hljs-keyword">implements</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">ServerSocketChannel</span> <span data-type="markclass" class="hljs-variable">serverSocketChannel</span> <span data-type="markclass" class="hljs-operator">=</span> </code><code data-type="codeline">                    (ServerSocketChannel) selectionKey.channel();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> serverSocketChannel.accept();</code><code data-type="codeline">                System.out.println(<span data-type="markclass" class="hljs-string">"获取一个链接："</span> + channel.getRemoteAddress());</code><code data-type="codeline">                channel.configureBlocking(<span data-type="markclass" class="hljs-literal">false</span>);</code><code data-type="codeline">                channel.register(ioSelector, </code><code data-type="codeline">                                 SelectionKey.OP_READ, ByteBuffer.allocate(<span data-type="markclass" class="hljs-number">1024</span>));</code><code data-type="codeline">                ioSelector.wakeup();</code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                e.printStackTrace();</code><code data-type="codeline">            }</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">private</span> <span data-type="markclass" class="hljs-keyword">class</span> <span data-type="markclass" class="hljs-title class_">ProcessHander</span> <span data-type="markclass" class="hljs-keyword">implements</span> <span data-type="markclass" class="hljs-title class_">Hander</span> {</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">        <span data-type="markclass" class="hljs-meta">@Override</span></code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">process</span><span data-type="markclass" class="hljs-params">(SelectionKey selectionKey)</span> {</code><code data-type="codeline">            <span data-type="markclass" class="hljs-keyword">try</span> {</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">SocketChannel</span> <span data-type="markclass" class="hljs-variable">channel</span> <span data-type="markclass" class="hljs-operator">=</span> (SocketChannel) selectionKey.channel();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">ByteBuffer</span> <span data-type="markclass" class="hljs-variable">buffer</span> <span data-type="markclass" class="hljs-operator">=</span> ByteBuffer.allocate(<span data-type="markclass" class="hljs-number">1024</span>);</code><code data-type="codeline">                buffer.clear();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">StringBuilder</span> <span data-type="markclass" class="hljs-variable">sb</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">StringBuilder</span>();</code><code data-type="codeline">                <span data-type="markclass" class="hljs-type">int</span> <span data-type="markclass" class="hljs-variable">read</span> <span data-type="markclass" class="hljs-operator">=</span> <span data-type="markclass" class="hljs-number">0</span>;</code><code data-type="codeline">                <span data-type="markclass" class="hljs-keyword">if</span> ((read = channel.read(buffer)) &gt; <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">                    buffer.flip();</code><code data-type="codeline">                    sb.append(Charset.forName(<span data-type="markclass" class="hljs-string">"UTF-8"</span>).newDecoder().decode(buffer));</code><code data-type="codeline">                    buffer.clear();</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">else</span> <span data-type="markclass" class="hljs-keyword">if</span> (read == <span data-type="markclass" class="hljs-number">0</span>) {</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-keyword">return</span>;</code><code data-type="codeline">                } <span data-type="markclass" class="hljs-keyword">else</span> <span data-type="markclass" class="hljs-keyword">if</span> (read == -<span data-type="markclass" class="hljs-number">1</span>) {</code><code data-type="codeline">                    <span data-type="markclass" class="hljs-keyword">if</span> (selectionKey.isValid()) {</code><code data-type="codeline">                        selectionKey.cancel();</code><code data-type="codeline">                        channel.close();</code><code data-type="codeline">                    }</code><code data-type="codeline">                }</code><code data-type="codeline">                System.out.printf(<span data-type="markclass" class="hljs-string">"收到 %s 发来的：%s\n"</span>, </code><code data-type="codeline">                                  channel.getRemoteAddress(), sb);</code><code data-type="codeline">                buffer.clear();</code><code data-type="codeline">                buffer.put((<span data-type="markclass" class="hljs-string">"收到，你发来的是："</span> + sb + <span data-type="markclass" class="hljs-string">"\r\n"</span>).getBytes(<span data-type="markclass" class="hljs-string">"utf-8"</span>));</code><code data-type="codeline">                buffer.flip();</code><code data-type="codeline">                channel.write(buffer);</code><code data-type="codeline">                System.out.printf(<span data-type="markclass" class="hljs-string">"回复 %s 内容是： %s\n"</span>, </code><code data-type="codeline">                                  channel.getRemoteAddress(), sb);</code><code data-type="codeline">                channel.register(ioSelector, SelectionKey.OP_READ, buffer.clear());</code><code data-type="codeline">            } <span data-type="markclass" class="hljs-keyword">catch</span> (IOException e) {</code><code data-type="codeline">                e.printStackTrace();</code><code data-type="codeline">            }</code><code data-type="codeline">        }</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">    <span data-type="markclass" class="hljs-keyword">public</span> <span data-type="markclass" class="hljs-keyword">static</span> <span data-type="markclass" class="hljs-keyword">void</span> <span data-type="markclass" class="hljs-title function_">main</span><span data-type="markclass" class="hljs-params">(String[] args)</span> <span data-type="markclass" class="hljs-keyword">throws</span> IOException {</code><code data-type="codeline">        <span data-type="markclass" class="hljs-keyword">new</span> <span data-type="markclass" class="hljs-title class_">ReactorDemo</span>().startServer();</code><code data-type="codeline">    }</code><code data-type="codeline"><br class="ProseMirror-trailingBreak"></code><code data-type="codeline">}</code></pre></div></div></div></div></div> <div class="simplebar-placeholder" style="width: 770px; height: 3030px;"></div></div> <div class="simplebar-track simplebar-horizontal" style="visibility: hidden;"><div class="simplebar-scrollbar simplebar-visible" style="width: 0px; display: none;"></div></div> <div class="simplebar-track simplebar-vertical" style="visibility: visible;"><div class="simplebar-scrollbar simplebar-visible" style="height: 0px; display: none;"></div></div></div></div> <div data-codeblock-copy="">复制代码</div> <div data-codeblock-index=""><div data-codeblock-number="1"></div><div data-codeblock-number="2"></div><div data-codeblock-number="3"></div><div data-codeblock-number="4"></div><div data-codeblock-number="5"></div><div data-codeblock-number="6"></div><div data-codeblock-number="7"></div><div data-codeblock-number="8"></div><div data-codeblock-number="9"></div><div data-codeblock-number="10"></div><div data-codeblock-number="11"></div><div data-codeblock-number="12"></div><div data-codeblock-number="13"></div><div data-codeblock-number="14"></div><div data-codeblock-number="15"></div><div data-codeblock-number="16"></div><div data-codeblock-number="17"></div><div data-codeblock-number="18"></div><div data-codeblock-number="19"></div><div data-codeblock-number="20"></div><div data-codeblock-number="21"></div><div data-codeblock-number="22"></div><div data-codeblock-number="23"></div><div data-codeblock-number="24"></div><div data-codeblock-number="25"></div><div data-codeblock-number="26"></div><div data-codeblock-number="27"></div><div data-codeblock-number="28"></div><div data-codeblock-number="29"></div><div data-codeblock-number="30"></div><div data-codeblock-number="31"></div><div data-codeblock-number="32"></div><div data-codeblock-number="33"></div><div data-codeblock-number="34"></div><div data-codeblock-number="35"></div><div data-codeblock-number="36"></div><div data-codeblock-number="37"></div><div data-codeblock-number="38"></div><div data-codeblock-number="39"></div><div data-codeblock-number="40"></div><div data-codeblock-number="41"></div><div data-codeblock-number="42"></div><div data-codeblock-number="43"></div><div data-codeblock-number="44"></div><div data-codeblock-number="45"></div><div data-codeblock-number="46"></div><div data-codeblock-number="47"></div><div data-codeblock-number="48"></div><div data-codeblock-number="49"></div><div data-codeblock-number="50"></div><div data-codeblock-number="51"></div><div data-codeblock-number="52"></div><div data-codeblock-number="53"></div><div data-codeblock-number="54"></div><div data-codeblock-number="55"></div><div data-codeblock-number="56"></div><div data-codeblock-number="57"></div><div data-codeblock-number="58"></div><div data-codeblock-number="59"></div><div data-codeblock-number="60"></div><div data-codeblock-number="61"></div><div data-codeblock-number="62"></div><div data-codeblock-number="63"></div><div data-codeblock-number="64"></div><div data-codeblock-number="65"></div><div data-codeblock-number="66"></div><div data-codeblock-number="67"></div><div data-codeblock-number="68"></div><div data-codeblock-number="69"></div><div data-codeblock-number="70"></div><div data-codeblock-number="71"></div><div data-codeblock-number="72"></div><div data-codeblock-number="73"></div><div data-codeblock-number="74"></div><div data-codeblock-number="75"></div><div data-codeblock-number="76"></div><div data-codeblock-number="77"></div><div data-codeblock-number="78"></div><div data-codeblock-number="79"></div><div data-codeblock-number="80"></div><div data-codeblock-number="81"></div><div data-codeblock-number="82"></div><div data-codeblock-number="83"></div><div data-codeblock-number="84"></div><div data-codeblock-number="85"></div><div data-codeblock-number="86"></div><div data-codeblock-number="87"></div><div data-codeblock-number="88"></div><div data-codeblock-number="89"></div><div data-codeblock-number="90"></div><div data-codeblock-number="91"></div><div data-codeblock-number="92"></div><div data-codeblock-number="93"></div><div data-codeblock-number="94"></div><div data-codeblock-number="95"></div><div data-codeblock-number="96"></div><div data-codeblock-number="97"></div><div data-codeblock-number="98"></div><div data-codeblock-number="99"></div><div data-codeblock-number="100"></div><div data-codeblock-number="101"></div><div data-codeblock-number="102"></div><div data-codeblock-number="103"></div><div data-codeblock-number="104"></div><div data-codeblock-number="105"></div><div data-codeblock-number="106"></div><div data-codeblock-number="107"></div><div data-codeblock-number="108"></div><div data-codeblock-number="109"></div><div data-codeblock-number="110"></div><div data-codeblock-number="111"></div><div data-codeblock-number="112"></div><div data-codeblock-number="113"></div><div data-codeblock-number="114"></div><div data-codeblock-number="115"></div><div data-codeblock-number="116"></div><div data-codeblock-number="117"></div><div data-codeblock-number="118"></div><div data-codeblock-number="119"></div><div data-codeblock-number="120"></div><div data-codeblock-number="121"></div><div data-codeblock-number="122"></div><div data-codeblock-number="123"></div><div data-codeblock-number="124"></div><div data-codeblock-number="125"></div><div data-codeblock-number="126"></div><div data-codeblock-number="127"></div><div data-codeblock-number="128"></div><div data-codeblock-number="129"></div><div data-codeblock-number="130"></div><div data-codeblock-number="131"></div><div data-codeblock-number="132"></div><div data-codeblock-number="133"></div><div data-codeblock-number="134"></div><div data-codeblock-number="135"></div><div data-codeblock-number="136"></div><div data-codeblock-number="137"></div><div data-codeblock-number="138"></div><div data-codeblock-number="139"></div><div data-codeblock-number="140"></div><div data-codeblock-number="141"></div><div data-codeblock-number="142"></div><div data-codeblock-number="143"></div><div data-codeblock-number="144"></div><div data-codeblock-number="145"></div><div data-codeblock-number="146"></div><div data-codeblock-number="147"></div><div data-codeblock-number="148"></div><div data-codeblock-number="149"></div><div data-codeblock-number="150"></div><div data-codeblock-number="151"></div></div></div></div><p data-type="paragraph">示例中创建了 selector 和 ioSelector ，其中 selector 只处理 socket 的建立，在 Acceptor.process 方法中把 socket 注册给 ioSelector。在 ProcessHander.process 方法中 ioSelector 只负责处理 IO 事件。这样，我们把 selector 进行了拆分。参考多线程实现，同理我们可以创建 N 个线程，处理 ioSelector 对应的 IO 事件。</p><div data-type="image"><div data-content="" data-style-width="100%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/eb3d02db85408fc538a033138e5f98e5.png"> <!----></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading9" data-type="heading" top="14182.2001953125">总结</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">至此，我们了解了 Reactor 的三种模型结果，分别是单 Reactor 单线程、单 Reactor 多线程、多 Reactor 多线程。所有代码不够严谨，只为了表示可以使用多个线程或者多个 selector 之间的关系。总结重点：</p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><ol data-type="numberedlist"><li><p data-number="1" data-type="paragraph">reactor 是一种设计模式</p></li><li><p data-number="2" data-type="paragraph">基于事件驱动来处理</p></li><li><p data-number="3" data-type="paragraph">利用多路分发的策略，让不同业务处理各司其职</p></li><li><p data-number="4" data-type="paragraph">有单线程，单 Reactor 多线程 和 多 Reactor 多线程，三种实现方式</p></li></ol><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">参考：</p><p data-type="paragraph"><a href="https://xie.infoq.cn/link?target=http%3A%2F%2Fgee.cs.oswego.edu%2Fdl%2Fcpjslides%2Fnio.pdf" title="" data-type="link">http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf</a><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading10" data-type="heading" top="14616.2001953125">系列</h2><p data-type="paragraph"><strong data-type="strong"><a href="https://xie.infoq.cn/article/0e36ad9712c8d9ad8f7a7c570" title="" data-type="link">NIO 看破也说破（一）—— Linux/IO 基础</a></strong><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><strong data-type="strong"><a href="https://xie.infoq.cn/article/e8ab7c9020253b83355c10661" title="" data-type="link">NIO 看破也说破（二）—— Java 中的两种 BIO</a></strong><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><strong data-type="strong"><a href="https://xie.infoq.cn/article/1f44643161e1666b6a30b85e7" title="" data-type="link">NIO 看破也说破（三）—— 不同的 IO 模型</a></strong><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><strong data-type="strong"><a href="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0" title="" data-type="link">NIO 看破也说破（四）—— Java 的 NIO</a></strong><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><a href="https://xie.infoq.cn/article/9e57819677d77f9a34852f6e9" title="" data-type="link">NIO 看破也说破（五）： 搞，今天就搞，搞懂Buffer</a><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><h2 data-id="heading11" data-type="heading" top="14870.2001953125">关注我</h2><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p><p data-type="paragraph">如果您在微信阅读，请您点击链接 <a href="https://xie.infoq.cn/link?target=https%3A%2F%2Fmp.weixin.qq.com%2Fmp%2Fprofile_ext%3Faction%3Dhome%26__biz%3DMzI3MDYwOTYwOA%3D%3D%26scene%3D110%23wechat_redirect" title="" data-type="link">关注我</a> ，如果您在 PC 上阅读请扫码关注我，欢迎与我交流随时指出错误 </p><div data-type="image"><div data-content="" data-style-width="25%" data-style-bordertype="boxShadow"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/3874eef202c2e5d161059dcf58e1430f.png"> <!----></div></div><p data-type="paragraph"><br class="ProseMirror-trailingBreak"></p></div></div><div data-v-759cadff="" data-v-fbbc0218="" class="selection-operation-bar note-bar popup-note-menu down"><div data-v-759cadff="" class="arrow down"></div><div data-v-759cadff="" class="main"><div data-v-759cadff="" class="menu line"><i data-v-a949bb52="" data-v-759cadff="" class="com-icon iconfont icon-hover" style="font-size: 20px; color: rgb(255, 255, 255);"></i><span data-v-759cadff="" class="text">划线</span></div><!----><div data-v-759cadff="" class="menu comment"><i data-v-a949bb52="" data-v-759cadff="" class="com-icon iconfont icon-hover" style="font-size: 20px; color: rgb(255, 255, 255);"></i><span data-v-759cadff="" class="text">评论</span></div><div data-v-759cadff="" class="menu copy"><i data-v-a949bb52="" data-v-759cadff="" class="com-icon iconfont icon-hover" style="font-size: 20px; color: rgb(255, 255, 255);"></i><span data-v-759cadff="" class="text">复制</span></div></div></div></div></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="author-information after-content"><div data-v-4a1c10f0="" data-v-b110a92e="" class="row"><span data-v-4a1c10f0="" data-v-b110a92e="" class="date">发布于: 2020-05-19</span><span data-v-4a1c10f0="" data-v-b110a92e="" class="views">阅读数: 3802</span></div></div><div data-v-4a1c10f0="" data-v-b110a92e="" class="copyright"><p data-v-4a1c10f0="" data-v-b110a92e="">版权声明: 本文为 InfoQ 作者【小眼睛聊技术】的原创文章。</p><p data-v-4a1c10f0="" data-v-b110a92e="">原文链接:【<a href="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0" target="_blank">https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0</a>】。文章转载请联系作者。</p></div><!----><div data-v-4300d745="" data-v-4a1c10f0="" class="com-label-list tag-list" data-v-b110a92e=""><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/3" com-label-title="" class="com-label-title">Java</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/189" com-label-title="" class="com-label-title">学习</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/211" com-label-title="" class="com-label-title">开源</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/238" com-label-title="" class="com-label-title">架构</a><a data-v-4426e0e8="" data-v-4300d745="" href="https://xie.infoq.cn/tag/531" com-label-title="" class="com-label-title">后端</a></div><div data-v-affe19d2="" data-v-4a1c10f0="" author-card="" class="com-author-card author-info" data-v-b110a92e=""><div data-v-affe19d2="" class="author-card"><div data-v-affe19d2="" class="top"><div data-v-affe19d2="" class="avatar"><div data-v-7b3025e7="" data-v-affe19d2="" class="com-avatar-wrap"><img data-v-7b3025e7="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/449a9571.jpg" alt="用户头像" class="com-avatar" style="width: 55px; height: 55px;"><!----></div></div><div data-v-affe19d2="" class="info"><div data-v-affe19d2="" class="top"><h3 data-v-affe19d2="" class="author-name"><span data-v-3bc80670="" data-v-6281d13f="" data-v-affe19d2="" class="ctn"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span></h3><div data-v-d2847db8="" data-v-affe19d2="" gk-button="" gkbtn-color="unfollow" gkbtn-size="small" class="geek-foll-btn Button_button_3onsJ" follow-button="" data-follow="0"><i data-v-affe19d2="" class="iconfont"></i>关注</div></div><p data-v-affe19d2="" class="mood"> 欢迎关注公众号“小眼睛聊技术” <span data-v-affe19d2="" class="dot"></span> 2018-11-12 加入</p></div></div><!----><p data-v-affe19d2="" class="intro">互联网老兵，关注产品、技术、管理</p></div></div><div data-v-4a51d84b="" data-v-4a1c10f0="" class="widget-operation-bottom widget-operation inner" info="[object Object]" wxshareurl="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0" data-v-b110a92e=""><!----><div data-v-4a51d84b="" class="like btn"><i data-v-4a51d84b="" class="iconfont"></i><!----></div><div data-v-4a51d84b="" class="favorite btn"><i data-v-4a51d84b="" class="iconfont"></i></div><!----><div data-v-4a51d84b="" class="weixin btn"><span data-v-4a51d84b="" class="gkui-popup share-popup-top share-popup"><i data-v-4a51d84b="" class="iconfont"></i><!----></span></div><div data-v-4a51d84b="" class="weibo btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" class="horde btn"><i data-v-4a51d84b="" class="iconfont"></i></div><div data-v-4a51d84b="" data-self="2" data-label="举报" class="extra btn"><i data-v-4a51d84b="" class="iconfont"></i></div><!----></div><!----><div data-v-b7e0eae6="" data-v-4a1c10f0="" class="article-comment-block comment-list-wrap" data-v-b110a92e=""><div data-v-b7e0eae6="" class="comment-list"><h2 data-v-b7e0eae6="">评论 <span data-v-b7e0eae6="" class="comment-number">(8 条评论)</span></h2><div data-v-b7e0eae6="" class="comment-form"><div data-v-b7e0eae6="" class="comment-input no-reference"><div data-v-045f148c="" data-v-b7e0eae6="" contenteditable="plaintext-only" spellcheck="false" placeholder="写下你的想法，一起交流" class="textarea comment-textarea empty"></div> <!----></div><div data-v-b7e0eae6="" class="comment-operation"><div data-v-b7e0eae6="" gk-button="" gkbtn-color="green" gkbtn-size="normal" disabled="disabled" class="comment-btn Button_button_3onsJ"> 发布 </div></div></div><div data-v-b7e0eae6="" class="list"><!----><div data-v-fcafb8d2="" data-v-54bade26="" data-v-b7e0eae6="" class="reply-item comment-item"><div data-v-54bade26="" data-v-fcafb8d2="" class="comment-avatar"><div data-v-7b3025e7="" data-v-54bade26="" class="com-avatar-wrap" data-v-fcafb8d2=""><img data-v-7b3025e7="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/497fbe0f.jpg" alt="用户头像" class="com-avatar" style="width: 40px; height: 40px;"><!----></div></div><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">Defu Li</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">写的太好了，目前看到将NIO这几个概念最清楚了（看了不下几十篇后的感想）</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2022-01-21 19:27 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----></div><div data-v-fcafb8d2="" data-v-54bade26="" data-v-b7e0eae6="" class="reply-item comment-item"><div data-v-54bade26="" data-v-fcafb8d2="" class="comment-avatar"><div data-v-7b3025e7="" data-v-54bade26="" class="com-avatar-wrap" data-v-fcafb8d2=""><img data-v-7b3025e7="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/4d780f27.jpg" alt="用户头像" class="com-avatar" style="width: 40px; height: 40px;"><!----></div></div><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">乐白</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">还是别忘了把之前几篇链接加到文章末尾哈。</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-05-19 17:00 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----><div data-v-54bade26="" data-v-fcafb8d2="" class="reply-list has-more"><span data-v-54bade26="" data-v-fcafb8d2=""><div data-v-fcafb8d2="" data-v-54bade26="" class="reply-item"><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">感谢提醒</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-05-19 20:53 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 1 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----></div><div data-v-fcafb8d2="" data-v-54bade26="" class="reply-item"><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="deleted">该评论已删除</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-05-22 13:10 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----></div><div data-v-fcafb8d2="" data-v-54bade26="" class="reply-item"><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">乐白</a><!----></span><span data-v-fcafb8d2="" class="reply-label">回复</span><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">正在研究，稍等。</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-05-22 14:16 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----></div></span><div data-v-54bade26="" data-v-fcafb8d2="" class="reply-more">查看更多回复</div></div></div><div data-v-fcafb8d2="" data-v-54bade26="" data-v-b7e0eae6="" class="reply-item comment-item"><div data-v-54bade26="" data-v-fcafb8d2="" class="comment-avatar"><div data-v-7b3025e7="" data-v-54bade26="" class="com-avatar-wrap" data-v-fcafb8d2=""><img data-v-7b3025e7="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/4d780f27.jpg" alt="用户头像" class="com-avatar" style="width: 40px; height: 40px;"><!----></div></div><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">乐白</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">第四篇如期而至，辛苦码字，InfoQ首页推荐。</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-05-19 17:00 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----><div data-v-54bade26="" data-v-fcafb8d2="" class="reply-list"><span data-v-54bade26="" data-v-fcafb8d2=""><div data-v-fcafb8d2="" data-v-54bade26="" class="reply-item"><div data-v-fcafb8d2="" class="reply-author"><span data-v-3bc80670="" data-v-6281d13f="" data-v-fcafb8d2="" class="ctn reply-user"><a data-v-3bc80670="" rel="nofollow" com-author-name="" class="com-author-name">小眼睛聊技术</a><!----></span><!----></div><div data-v-fcafb8d2="" class="reply-content"><span data-v-fcafb8d2="" class="">系列更新，求推荐</span><!----></div><div data-v-fcafb8d2="" class="reply-operation"><div data-v-fcafb8d2="" class="date">2020-06-04 16:04 <!----></div><div data-v-fcafb8d2="" class="reply-action"><span data-v-fcafb8d2="" class="like-btn"><i data-v-fcafb8d2="" class="iconfont"></i> 0 </span><span data-v-fcafb8d2="" class="reply-btn"><i data-v-fcafb8d2="" class="iconfont"></i><em data-v-fcafb8d2="">回复</em></span></div></div><!----></div></span><!----></div></div><div data-v-4c7bd645="" data-v-b7e0eae6="" class="com-more-button loading-btn"><span data-v-4c7bd645="" class="finish-label">没有更多了</span></div></div></div></div><!----></div><!----><div data-v-4449d9fe="" data-v-4a1c10f0="" id="gk-layer" gkmodal-color="common" class="report-modal Modal_gk-modal_3ly5T" data-v-b110a92e=""><!----><!----></div><div data-v-355438c6="" data-v-4a1c10f0="" id="gk-layer" gkmodal-color="base" class="share-weixin-modal Modal_gk-modal_3ly5T" data-v-b110a92e=""><!----><!----></div><div data-v-4b5aad81="" data-v-4a1c10f0="" id="gk-layer" gkmodal-color="base" class="modal-share-horde Modal_gk-modal_3ly5T" data-v-b110a92e=""><!----><!----></div><!----></div><!----><div data-v-0dc6ee4d="" data-v-4a1c10f0="" class="nps-score" data-v-b110a92e=""><div data-v-0dc6ee4d="" id="gk-layer" gkmodal-color="common" class="nps-modal Modal_gk-modal_3ly5T"><!----><!----></div><div data-v-0dc6ee4d="" id="gk-layer" gkmodal-color="common" class="nps-modal-thank Modal_gk-modal_3ly5T"><!----><!----></div></div></div><div data-v-b110a92e="" id="check-bottom-bar"></div><div data-v-b110a92e="" layout-footer-wrap="" class="layout-footer-wrap"><div data-v-c5384832="" data-v-b110a92e="" class="footer layout-footer"><ul data-v-c5384832="" class="layout-content"><li data-v-c5384832="" class="theme"><div data-v-c5384832="" class="logo"><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/logo.3bdee6e4.png" alt="logo"><p data-v-c5384832="">促进软件开发及相关领域知识与创新的传播</p></div></li><li data-v-c5384832=""><dl data-v-c5384832="" class="footer-nav"><dt data-v-c5384832="">InfoQ</dt><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://www.infoq.cn/about" target="_blank" rel="noopener nofollow">关于我们</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://www.infoq.cn/contribute" target="_blank" rel="noopener nofollow">我要投稿</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://www.geekbang.org/partner" target="_blank" rel="noopener nofollow">合作伙伴</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://www.lagou.com/gongsi/j43775.html" target="_blank" rel="noopener nofollow">加入我们</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://infoq.cn/official/account" target="_blank" rel="noopener nofollow">关注我们</a></dd></dl></li><li data-v-c5384832=""><dl data-v-c5384832="" class="footer-nav"><dt data-v-c5384832="">联系我们</dt><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="mailto:editors@geekbang.com" target="_blank" rel="noopener nofollow">内容投稿：editors@geekbang.com</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="mailto:hezuo@geekbang.com" target="_blank" rel="noopener nofollow">业务合作：hezuo@geekbang.com</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="mailto:feedback@geekbang.com" target="_blank" rel="noopener nofollow">反馈投诉：feedback@geekbang.com</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="mailto:zhaopin@geekbang.com" target="_blank" rel="noopener nofollow">加入我们：zhaopin@geekbang.com</a></dd><dd data-v-c5384832=""><!----><span data-v-c5384832="">联系电话：010-64738142</span></dd><dd data-v-c5384832=""><!----><span data-v-c5384832="">地址：北京市朝阳区望京北路9号2幢7层A701</span></dd></dl></li><li data-v-c5384832=""><dl data-v-c5384832="" class="footer-nav"><dt data-v-c5384832="">InfoQ 近期会议</dt><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://qcon.infoq.cn/2024/shanghai?utm_source=infoq&amp;utm_medium=footer" target="_blank" rel="noopener nofollow">上海 · QCon 全球软件开发大会 2024.10.18-19</a></dd><dd data-v-c5384832=""><!----><a data-v-c5384832="" href="https://aicon.infoq.cn/202412/beijing/?utm_source=infoq&amp;utm_medium=footer" target="_blank" rel="noopener nofollow">北京 · AICon 全球人工智能开发与应用大会 2024.12.13-14</a></dd></dl></li><li data-v-c5384832=""><dl data-v-c5384832="" class="footer-nav"><dt data-v-c5384832="">全球 InfoQ</dt><dd data-v-c5384832=""><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/55cd81623e36f5ab7a7db74d60b74838.png" alt="会议图片"><a data-v-c5384832="" href="https://www.infoq.com/" target="_blank" rel="noopener nofollow">InfoQ  En</a></dd><dd data-v-c5384832=""><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/95fe851c02c86120e9037eada6a36d13.png" alt="会议图片"><a data-v-c5384832="" href="https://www.infoq.com/jp/" target="_blank" rel="noopener nofollow">InfoQ Jp</a></dd><dd data-v-c5384832=""><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/2aa440b6d94e94f64c508f16da38933e.png" alt="会议图片"><a data-v-c5384832="" href="http://www.infoq.com/fr/" target="_blank" rel="noopener nofollow">InfoQ Fr</a></dd><dd data-v-c5384832=""><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/4e737ce82bc7c8a1c2f2307bcea9a11e.png" alt="会议图片"><a data-v-c5384832="" href="http://www.infoq.com/br/" target="_blank" rel="noopener nofollow">InfoQ Br</a></dd></dl></li></ul><div data-v-c5384832="" class="copyright"> Copyright © 2024, Geekbang Technology Ltd. All rights reserved. 极客邦控股（北京）有限公司 | <a data-v-c5384832="" target="_blank" href="https://beian.miit.gov.cn/" class="icp"> 京 ICP 备 16027448 号 - 5</a><a data-v-c5384832="" target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010502039052"><img data-v-c5384832="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/beian.d0289dc0.png" alt="京公网安备"><span data-v-c5384832="">京公网安备 11010502039052号</span></a><a data-v-c5384832="" target="_blank" href="https://time.geekbang.org/hybrid/certificates" class="icp"> | 产品资质</a></div></div></div><div data-v-b110a92e="" id="gk-layer" gkmodal-color="common" class="offline-modal Modal_gk-modal_3ly5T"><!----><!----></div></div>
<img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/logo-121-75.yuij86g.png" alt="NIO看破也说破（四）—— Java的NIO_Java_小眼睛聊技术_InfoQ写作社区" style="display: none;">
<script>
    (function (i, s, o, g, r, a, m) {
        i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
            (i[r].q = i[r].q || []).push(arguments)
        }, i[r].l = 1 * new Date(); a = s.createElement(o),
            m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
    })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
    ga('create', 'UA-103082599-11', 'auto');
    ga('send', 'pageview');
</script>
<script>
    var isRobot = /ltdgeekbang/i.test(window.navigator.userAgent.toLowerCase())
    if (!isRobot) {
        var __gBaiduSiteIdConfig__ = {
            infoq: '094d2af1d9a57fd9249b3fa259428445',
            xie: 'c53c258392315152cda6662d99774537'
        };
        var _hmt = _hmt || [];
        _hmt.push(['_setAccount', __gBaiduSiteIdConfig__.infoq]);
        _hmt.push(['_setAutoPageview', true]);
        _hmt.push(['_setAccount', __gBaiduSiteIdConfig__.xie]);
        _hmt.push(['_setAutoPageview', true]);

        (function() {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?" + __gBaiduSiteIdConfig__.infoq;
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(hm, s);
        })();
        
        (function() {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?" + __gBaiduSiteIdConfig__.xie;
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(hm, s);
        })();
        
    }
</script>
<script>
    
    var isSmart = /swan\//.test(window.navigator.userAgent) || /^webswan-/.test(window.name);
    if (isSmart) {
        var smart = document.createElement("script");
        smart.src = "https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.21.js";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(smart, s);
    }
</script>
<script>
    (function(para) {
        var p = para.sdk_url, n = para.name, w = window, d = document, s = 'script',x = null,y = null;
        if(typeof(w['sensorsDataAnalytic201505']) !== 'undefined') {
            return false;
        }
        w['sensorsDataAnalytic201505'] = n;
        w[n] = w[n] || function(a) {return function() {(w[n]._q = w[n]._q || []).push([a, arguments]);}};
        var ifs = ['track','quick','register','registerPage','registerOnce','trackSignup', 'trackAbtest', 'setProfile','setOnceProfile','appendProfile', 'incrementProfile', 'deleteProfile', 'unsetProfile', 'identify','login','logout','trackLink','clearAllRegister','getAppStatus'];
        for (var i = 0; i < ifs.length; i++) {
            w[n][ifs[i]] = w[n].call(null, ifs[i]);
        }
        if (!w[n]._t) {
            x = d.createElement(s), y = d.getElementsByTagName(s)[0];
            x.async = 1;
            x.src = p;
            x.setAttribute('charset','UTF-8');
            w[n].para = para;
            y.parentNode.insertBefore(x, y);
        }
    })({
        sdk_url: 'https://static.sensorsdata.cn/sdk/1.15.15/sensorsdata.min.js',
        heatmap_url: 'https://static.sensorsdata.cn/sdk/1.15.15/heatmap.min.js',
        name: 'sensors',
        server_url: 'https://sscentry.geekbang.org/sa?project=production',
        show_log: false,
        is_track_single_page: true,
        heatmap: {
            clickmap: 'not_collect',
            scroll_notice_map: 'not_collect'
        },
        preset_properties: {
            latest_referrer_host: true,
            latest_landing_page: true,
            url: true
        }
    });
</script>
<script>
    (function(win, export_obj) {
        win['LogAnalyticsObject'] = export_obj;
        if (!win[export_obj]) {
            function _collect() {
                _collect.q.push(arguments);
            }
            _collect.q = _collect.q || [];
            win[export_obj] = _collect;
        }
        win[export_obj].l = +new Date();
    })(window, 'collectEvent');
</script>
<script async="" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/collect-rangers-v5.1.4.js.下载"></script>

    <script type="text/javascript" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/main.js.下载"></script>



<div id="mantisImgViewBg" style="display: none">
			<div id="mantisImgView_header">
				<img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/rotateIcon.png" alt="" id="leftTurn">
				<img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/rotateIcon.png" alt="" id="rightTurn">
				<img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/blowUpIcon.png" alt="" id="blowUp">
				<img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/minificationIcon.png" alt="" id="minification">
			</div>
			<div id="imgBox">
				<img id="viewImg" src="https://xie.infoq.cn/article/b9baa25c9d506e4a1cb459fe0" alt="">
			</div>
		</div><iframe id="MANTIS-CHAT-DIV" frameborder="no" style="z-index: 2147483646; position: fixed; width: 0; height: 0;box-shadow: 0 2px 25px 0 rgba(0,0,0,.3);" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/saved_resource.html"></iframe><link type="text/css" rel="stylesheet" href="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/PCMiniStyle.css"><div id="pcMsgTip" style="display:none;"><div class="pcMsgTipBox"><img id="pcMsgTipGB" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/close(1).png" alt="" onclick="mantisOnClosePCMsgList()"><div id="pcMsgTipMsgList" onclick="mantis.requestChat()"></div></div><span class="pcMsgTipAfter"> </span></div><div id="MANTIS_INV_INNER_DIV" class="mantisPcInvite_center" style="display:none;left: 50%;transform: translate(-50%,-50%);"><img src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/closeInviteIcon.png" class="closeInviteIcon" onclick="mantisHideInvite()"><img style="width:380px" src="./NIO看破也说破（四）—— Java的NIO_InfoQ_files/pcInvite.png" onclick="mantis.requestChat()"></div><div data-v-742c2edc="" id="gkui-message-list" style="top: 24px;"><span data-v-742c2edc=""></span></div><div id="gkui-modal-controller"><!----></div><div class="common-login-modal"><div data-v-66e08e49="" id="gk-layer" gkmodal-color="base" class="modal-login Modal_gk-modal_3ly5T"><!----><!----></div><div data-v-95b8db28="" id="gk-layer" gkmodal-color="base" class="modal-guide Modal_gk-modal_3ly5T"><!----><!----></div><div data-v-0b61e37e="" id="gk-layer" gkmodal-color="base" class="modal-receive-success Modal_gk-modal_3ly5T"><!----><!----></div><div data-v-de1e5142="" id="gk-layer" gkmodal-color="base" class="modal-rebind Modal_gk-modal_3ly5T"><!----><!----></div></div><div tabindex="-1" role="dialog" aria-hidden="true" class="pswp pswp--zoom-allowed"><div class="pswp__bg"></div><div class="pswp__scroll-wrap"><div class="pswp__container"><div class="pswp__item"></div><div class="pswp__item"></div><div class="pswp__item"></div></div><div class="pswp__ui pswp__ui--hidden"><div class="pswp__top-bar"><div class="pswp__counter"></div><button title="Close (Esc)" class="pswp__button pswp__button--close"></button><button title="Share" class="pswp__button pswp__button--share"></button><button title="Toggle fullscreen" class="pswp__button pswp__button--fs"></button><button title="Zoom in/out" class="pswp__button pswp__button--zoom"></button><div class="pswp__preloader"><div class="pswp__preloader__icn"><div class="pswp__preloader__cut"><div class="pswp__preloader__donut"></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap"><div class="pswp__share-tooltip"></div></div><button title="Previous (arrow left)" class="pswp__button pswp__button--arrow--left"></button><button title="Next (arrow right)" class="pswp__button pswp__button--arrow--right"></button><div class="pswp__caption"><div class="pswp__caption__center"></div></div></div></div></div><div><div id="gk-layer" gkmodal-color="logPhone" class="Modal_gk-modal_90vUO"><!----><!----></div></div><div><template shadowrootmode="closed"><link rel="stylesheet" href="chrome-extension://ghgabhipcejejjmhhchfonmamedcbeod/fcss41/ebtn41.css"><svg width="16px" height="16px" hide=""><path id="__path" d="M16 14V2a2 2 0 00-2-2H2a2 2 0 00-2 2v12c0 1.1.9 2 2 2h12a2 2 0 002-2z"></path><path fill="#f9f6e8" d="M12.9 12.2c.2-.2.3-.5.3-.8 0-.6-.5-1.2-1.2-1.2-.3 0-.6.1-.9.4-.7.7-1.7 1-2.6 1-2 0-3.8-1.6-3.8-3.6s1.7-3.6 3.8-3.6c1 0 2 .3 2.6 1 .3.3.6.4.9.4.7 0 1.3-.5 1.3-1.2 0-.3-.2-.6-.4-.8A6.4 6.4 0 008.5 2a6.1 6.1 0 00-6.3 6c0 3.3 2.8 6 6.3 6 1.6 0 3.2-.6 4.4-1.8z"></path></svg></template></div><!----><!----><!----><!----><!----><!----><!----><!----><!----><!----><img src="" id="page-complete" style="display: none;"></body></html>